feat(auth-ui): reusable AuthPanel (React + vanilla) + alias-publish - #458
Merged
Conversation
Safety checkpoint before implementation: package.json, tsconfig*.json, vite.config.ts, README, stub src/index.ts. Mirrors packages/identity-ui's build setup. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session-Id: 548a2d55-e1cd-4b5e-bfc4-b6e102bf4695
Types derived from @fuzefront/security-client's SessionResult union rather than hand-redefined. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session-Id: 548a2d55-e1cd-4b5e-bfc4-b6e102bf4695
Sign-in/sign-up form with mode toggle, per-action pending labels, error/mfa notice, and optional Google button — design-system-first (Button, Input, Alert, SeamDivider, CenteredCard only), transport-injected (no useLanguage/ useCurrentUser/window.location/asset imports). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session-Id: 548a2d55-e1cd-4b5e-bfc4-b6e102bf4695
window.FuzeFrontAuthUI.mount(container, opts) reimplements the same behavior/CSS classes as plain DOM, no React/React-DOM in the bundle. social defaults OFF for the vanilla entry point (React AuthPanel defaults social on). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session-Id: 548a2d55-e1cd-4b5e-bfc4-b6e102bf4695
Covers sign-in/signup submit, mode toggle, pending labels, mfa_required notice/callback, error alert, and Google button visibility gating (present only with startSocial AND methods.social advertising it; vanilla defaults social off even when startSocial is supplied). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session-Id: 548a2d55-e1cd-4b5e-bfc4-b6e102bf4695
Mirrors .github/workflows/security-packages-publish.yml: idempotent, owner-gated (izzywdev), no git push/tag. Adds packages/auth-ui to the root workspaces list. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session-Id: 548a2d55-e1cd-4b5e-bfc4-b6e102bf4695
Contributor
Automated code review (gate-code-review)Credit balance is too low Report-only — this check never blocks merge. |
CI runs `npm ci`, which requires package-lock.json to be in sync. The new package's devDeps (esbuild, vite, vitest, jsdom) were never added to the root lock, so `npm ci` failed at install — cascading to every downstream gate (frontend-build, unit tests, audit). Regenerated with `npm install --package-lock-only` (metadata only; no native-binary fetch, so unaffected by the os=linux .npmrc pin). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session-Id: 548a2d55-e1cd-4b5e-bfc4-b6e102bf4695
Contributor
Automated code review (gate-code-review)Credit balance is too low Report-only — this check never blocks merge. |
Collateral from this PR's lockfile update: regenerating package-lock.json deduped @types/node, so chat-service now resolves 24.x, where `Dirent` became generic. The annotation `Awaited<ReturnType<typeof fs.readdir>>` selected the Buffer overload, typing `entry.name` as Buffer and breaking `.startsWith` / `.toLowerCase` (index-docs.ts type-check, TS2322/2345/2367/2339). Drop the fragile annotation and infer the element type from the default (utf8) readdir call → Dirent<string>. Version-robust (compiles under @types/node 18 and 24) and behavior-identical. Verified locally: the index-docs Dirent errors are gone (remaining local tsc noise is this worktree's partial install, absent in CI). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session-Id: 548a2d55-e1cd-4b5e-bfc4-b6e102bf4695
Contributor
Automated code review (gate-code-review)Credit balance is too low Report-only — this check never blocks merge. |
izzywdev
pushed a commit
that referenced
this pull request
Jul 29, 2026
…r-menu-clplz8 Third merge from master. Two conflicts, both resolved in master's favour, plus one latent defect of my own that the merge brought into view. **`services/chat-service/src/rag/index-docs.ts`** — master fixed the SAME `Awaited<ReturnType<typeof fs.readdir>>` bug I had just fixed, with the same root-cause diagnosis (ReturnType resolves to readdir's Buffer overload under @types/node 24) and an equivalent remedy: infer from the call rather than annotate. Took master's version byte-for-byte and dropped my now-unneeded `type Dirent` import. It is their file and their fix; divergence here buys nothing. **`package-lock.json`** — master removed the stale nested `services/chat-service/node_modules/@types/node@18.19.0`, which is exactly the lockfile reconciliation my previous commit flagged as needed but out of scope. Resolved by taking master's lock wholesale and re-running `npm install` to re-derive this branch's `services/notification-service` subtree, rather than hand-editing lock JSON. Net effect: there is now exactly ONE `@types/node` in the tree, the root `24.13.3` — every nested Node-18 copy is gone, so notification- service resolves the version its manifest asks for and the class of failure that broke CI cannot recur silently in these workspaces. **`frontend/e2e/post-prod/live-smoke.spec.ts`** — not a conflict; a spec my earlier sweep missed because it lives under `e2e/post-prod/` rather than `tests/`. It seeded the bare `authToken` key before app boot. That still boots today, but only because the account vault's one-time legacy migration sweeps it — the same "upgrade path used as a write channel" trap already fixed in WhiteLabelLoginCard. This one matters more: it is the POST-PRODUCTION synthetic that verifies the live deployment, so it would have gone quietly red against prod the day that migration is retired. Now routed through the shared `seedMockSession` helper, whose docstring is generalised since it now seeds a real prod token as well as fixtures. Verified after the merge: - frontend `tsc --noEmit` clean; vitest 136/136 across 19 files - backend 91/91 (app-installations, notification-proxy, apps, provisioning, root-org-admin) against real Postgres, `--runInBand` - chat-service 131 passed / 2 skipped across 24 suites; `tsc` clean - notification-service `tsc` clean, 35/35, now resolving root 24.13.3 - master's new `@fuzefront/auth-ui` 17/17 - no duplicate migration ordinals: 014, 015, 016, 017 - `packages/auth-ui` touches no storage, so #458 introduces no bare-token write Note for anyone running the backend suites locally: master's Permit ReBAC work makes `PERMIT_API_KEY` mandatory at import time, so they need `PERMIT_API_KEY=ci-no-real-permit-calls` (what CI passes) or every suite fails in setup before a single test runs. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014KynhzG6wKxUnR8KWwK8un
izzywdev
pushed a commit
that referenced
this pull request
Jul 29, 2026
Adds the security-client -> auth-ui build chain the frontend type-check now depends on (tsconfig resolves auth-ui to its dist/index.d.ts), plus auth-ui's own type-check + vitest run — neither package had any CI coverage before this (PR #458 wired the publish workflow but not ci.yml). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session-Id: 548a2d55-e1cd-4b5e-bfc4-b6e102bf4695
4 tasks
izzywdev
pushed a commit
that referenced
this pull request
Jul 29, 2026
Pre-existing bug in PR #458, uncaught until now because auth-ui had no type-check step in ci.yml (only added in this PR). input.autocomplete is typed as the strict DOM `AutoFill` token union; a plain `string` param can't assign to it. Use setAttribute instead, which accepts any string and the property getter reflects regardless. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session-Id: 548a2d55-e1cd-4b5e-bfc4-b6e102bf4695
izzywdev
pushed a commit
that referenced
this pull request
Aug 2, 2026
…eed with the server's real default
"Sign-in with Google seems to disappear. It doesn't show in some cases."
Root cause: three places render the Google button gated on `authMethods.social.includes('google')`, and all three fall back to a LOCAL guess of that value while `GET /v1/security/methods` is in flight, or if it fails outright. That guess was `social: []`.
The server's actual default (routes/security.ts) is the opposite: Google is ENABLED unless `SECURITY_SOCIAL_GOOGLE` is explicitly set to `'false'`, and no deploy env sets it. So the fallback didn't describe "we don't know yet" — it confidently described a DIFFERENT, wrong state. Every time that fetch was slow, raced a fast unmount, or errored, a legitimately available Google button silently vanished for the duration of that render, with nothing resembling an error to explain it. That is exactly "some cases" — it depends on network timing, not on any deliberate server config.
Fixed in all three places that duplicate this fallback:
- frontend/src/pages/LoginPage.tsx (FALLBACK_METHODS)
- packages/auth-ui/src/components/AuthPanel.tsx (FALLBACK_METHODS) — the
new reusable panel from #458, same bug, same shape
- packages/auth-ui/src/vanilla/mount.ts (FALLBACK_METHODS) — the
non-React embed; its SEPARATE `social` opt-in default (OFF, intentional,
documented) is untouched, only the "what does the server actually do"
guess changes
Each fallback now sets `social: ['google']`, matching the security service's
real default. If that default is ever changed, the fallback must be updated
to match — it describes reality, not a guess, and a stale one would fail
the exact same way in the opposite direction.
Tests: one new case per surface (LoginPage, AuthPanel, vanilla mount) —
GET /methods rejecting must still render/show the Google button, not hide
it. Existing "no Google when methods explicitly return social: []" cases
are untouched (still resolve, still hide correctly) — only the FAILURE
path changes.
Verified: auth-ui package 19/19 tests pass; frontend suite unchanged
against master except the new test (66 -> 68, +2 across LoginPage and
this PR's earlier oidc-google-signin.test.ts addition).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QhfKNASYBD9aS3Wu6RUPfZ
33 tasks
izzywdev
added a commit
that referenced
this pull request
Aug 2, 2026
…d-trip (#459) Google button: three surfaces (LoginPage, the shared AuthPanel from #458, and the vanilla mount) rendered the button gated on a local fallback used while GET /methods is loading or if it fails. That fallback said social: [], but the server's real default is Google ENABLED unless SECURITY_SOCIAL_GOOGLE is explicitly 'false' (no deploy env sets it). Any slow fetch, unmount race, or outright failure silently hid an available button. Fixed all three fallbacks to match the server's real default; added a failing-fetch test per surface. userinfo round-trip: handleCallback() now reads claims from the ID token (tokenSet.claims()) instead of making a second HTTP call to userinfo. include_claims_in_id_token: true plus the openid/email/profile scope mappings already put every field syncUserToDatabase reads into the token returned by the exchange, verified by the same signature/issuer/audience checks. Falls back to a real userinfo call when email is absent, since email is the key user-sync matches accounts on. Removes one of six sequential provider round-trips from every password login and social callback. Verified via a clean git worktree of origin/master (not stash): backend/ security 31 suites identical pass/fail set, frontend 12 suites identical set plus the new test, packages/auth-ui 19/19, oidc-google-signin.test.ts 45/45. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QhfKNASYBD9aS3Wu6RUPfZ
izzywdev
added a commit
that referenced
this pull request
Aug 2, 2026
…465) * refactor(frontend): LoginPage consumes @fuzefront/auth-ui AuthPanel WIP checkpoint: LoginPage.tsx is now a thin adapter around AuthPanel (variant="compact" — .auth-form already supplies the card chrome). The AuthTransport wraps authAPI and reproduces the prior error-message taxonomy so AuthPanel's generic Error.message rendering shows identical wording. Page-load social-callback handling stays in LoginPage (page routing, not a panel concern). Config wiring (tsconfig/vite/vitest aliases, CI build steps) and test updates follow in subsequent commits. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session-Id: 548a2d55-e1cd-4b5e-bfc4-b6e102bf4695 * build(frontend): wire @fuzefront/auth-ui as an in-repo workspace package Resolve @fuzefront/auth-ui from SOURCE for vite/vitest bundling+tests (mirroring identity-ui), and to its built dist/index.d.ts for the frontend type-check (mirroring identity-ui/chat-ui/i18n) — it's TSX and imports @fuzefront/design-system, so type-checking its source under the frontend's own tsc would pull a second @types/react/csstype copy. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session-Id: 548a2d55-e1cd-4b5e-bfc4-b6e102bf4695 * ci(frontend): build + verify @fuzefront/auth-ui in the CI pipeline Adds the security-client -> auth-ui build chain the frontend type-check now depends on (tsconfig resolves auth-ui to its dist/index.d.ts), plus auth-ui's own type-check + vitest run — neither package had any CI coverage before this (PR #458 wired the publish workflow but not ci.yml). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session-Id: 548a2d55-e1cd-4b5e-bfc4-b6e102bf4695 * test(frontend): update LoginPage tests for the AuthPanel adapter - Removes LoginPage.signup-validation.test.tsx: it asserted a confirm-password field, password-policy-gated submit, and inline email-availability check, none of which @fuzefront/auth-ui's AuthPanel (v0.1.0) implements. Reintroducing them as one-off LoginPage markup would refork logic AuthPanel is meant to own; tracked as a fast-follow against the package instead. - Adds LoginPage.auth-panel-adapter.test.tsx covering the new adapter boundary: signup submits through the transport and redirects, a rejected signup surfaces the mapped friendly error (not the raw axios message), and a social-callback mfa_required result shows the page-level notice. LoginPage.signup-route.test.tsx, LoginPage.google-signin.test.tsx, and LoginPage.submit-resilience.test.tsx are unchanged and expected to keep passing — mode-detection, the Google/credentials paths, and the login error taxonomy (reproduced in LoginPage.tsx's mapAuthError, since AuthPanel just renders Error.message as-is) are all preserved. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session-Id: 548a2d55-e1cd-4b5e-bfc4-b6e102bf4695 * fix(auth-ui): satisfy AutoFill's strict type in the vanilla mount Pre-existing bug in PR #458, uncaught until now because auth-ui had no type-check step in ci.yml (only added in this PR). input.autocomplete is typed as the strict DOM `AutoFill` token union; a plain `string` param can't assign to it. Use setAttribute instead, which accepts any string and the property getter reflects regardless. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session-Id: 548a2d55-e1cd-4b5e-bfc4-b6e102bf4695 * fix(frontend): drop unused React import from LoginPage.tsx jsx: "react-jsx" means the React namespace import isn't needed, and noUnusedLocals (frontend/tsconfig.json) flags it as TS6133. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session-Id: 548a2d55-e1cd-4b5e-bfc4-b6e102bf4695 --------- Co-authored-by: fuzeone-bot[bot] <fuzeone-bot[bot]@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
FuzeFront had no reusable sign-in/sign-up component —
frontend/src/pages/LoginPage.tsxis a zero-prop route page welded toLanguageContext,useCurrentUser, an axios singleton, andwindow.location. FuzePicker's extension widget re-implemented the same auth form by hand (with a credential-leak bug). This package extracts the capability once, so both the app and the extension consume one component.What
New
@fuzefront/auth-uiwith two consumption modes from one design:AuthPanel(src/components/AuthPanel.tsx) — injection-seam props, none ofLoginPage's hidden coupling:transport: AuthTransport— host supplieslogin/signup/getAuthMethods/optionalstartSocial, so the same component runs against the app's axios client OR an extension'schrome.runtimebridge.variant: 'compact' | 'full'(full wraps inCenteredCard),mode,onAuthenticated,onMfaRequired,social,labels(all strings injected — i18n withoutuseLanguage).LoginPage: password sign-in/sign-up, mode toggle, per-action pending labels, error +mfa_requiredhandling, optional Google button (only whenstartSocialpresent and methods advertise it; Google mark keeps theds-conformance-allowexemption).@fuzefront/security-client(SessionResult,AuthenticatedSession,MfaRequiredChallenge,AuthMethods, …) — not redefined.dist/auth-ui.vanilla.js— a zero-dependency IIFE (esbuild.vanilla.mjs) exposingwindow.FuzeFrontAuthUI.mount(container, opts)/.unmount(), same markup + same token CSS classes, no React in the bundle. This is what the no-bundler Chrome extension embeds (socialdefaults off there)../styles.css— component classes, all values via fuse-seam DS tokens.exportsmap:.(React),./vanilla(IIFE),./styles.css. publishConfig → GitHub Packages, restricted.Two alias-publish workflows (mirroring
security-packages-publish.yml, owner-gatedizzywdev, idempotent, no git push/tag):auth-ui-packages-publish.yml:@fuzefront/auth-ui → @izzywdev/fuzefront-auth-uidesign-system-packages-publish.yml:@fuzefront/design-system → @izzywdev/fuzefront-design-system(unblocks the DS, which had never published — its canonical workflow is gated on thefuzefrontorg that doesn't own the repo).Verified
src/vanilla/{entry,mount}.tsimport only a type +DEFAULT_AUTH_LABELS; noreact/react-domimport anywhere undersrc/vanilla/. esbuild can only bundle what's imported, so the IIFE cannot contain React.exports,build(vite +tsc --emitDeclarationOnly+ esbuild vanilla),publishConfig, peerDeps (react18 +@fuzefront/design-system) all correct.NOT verified locally (CI is the check of record)
npm run build/type-check/testwere not run to green on the local Windows host: the FuzeFront monorepo.npmrcpinsos=linux(for CI Docker), so the installed esbuild native binary is@esbuild/aix-ppc64, not@esbuild/win32-x64, and the local build errors out on platform mismatch. This is the known repo gotcha, not a code issue — CI (ubuntu-latest) builds/tests authoritatively via the standard gates. Please confirm the gates go green before merge. Tests are written (AuthPanel.test.tsx,vanilla/mount.test.ts) but their pass/fail is CI's to report.Follow-ups (separate PRs)
LoginPage.tsxto consume<AuthPanel>(removes the duplication).Co-Authored-By: Claude Opus 4.8 noreply@anthropic.com